package com.itheima.stock.secuity.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.itheima.stock.pojo.SysUser;
import com.itheima.stock.utils.JwtTokenUtil;
import com.itheima.stock.vo.resp.LoginRespVo;
import com.itheima.stock.vo.result.R;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

/**
 * @author: siwukun
 * @date: 2022/06/15
 * @description:
 */
public class UserNamePasswordLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {


    private RedisTemplate redisTemplate;

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public UserNamePasswordLoginAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    /**
     * 认证成功处理方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //认证主体信息
        SysUser principal = (SysUser) authResult.getPrincipal();
        //组装信息响应给前端
        final String username = principal.getUsername();
        final Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
        //构建JwtToken
        final String token = JwtTokenUtil.createToken(username, authorities.toString());
        //组装响应结果
        final LoginRespVo respVo = LoginRespVo.builder().id(principal.getId())
                .nickName(principal.getNickName())
                .username(principal.getUsername())
                .phone(principal.getPhone())
                .menus(principal.getMenus())
                .permissions(principal.getPermissions())
                .accessToken(token).build();
        //设置响应格式
        response.setContentType("text/html;charSet=UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        R<LoginRespVo> ok = R.ok(respVo);
        //序列化转换成json响应给前端
        response.getWriter().write(new ObjectMapper().writeValueAsString(ok));
    }


    /**
     * 用户登录认证权限过滤器
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //从post请求流中获取登录信息
        String username=null;
        String password=null;
        String code = null;
        String rKey = null;
        //判断 是否是ajax登录
        if (request.getMethod().equalsIgnoreCase("POST")||request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            //获取请求流中的json数据
            ServletInputStream stream = request.getInputStream();
            //反序列化
            HashMap<String,String> map = new ObjectMapper().readValue(stream, HashMap.class);
            username=map.get("username");
            password=map.get("password");
            code = map.get("code");
            rKey=map.get("rkey");

        }else {
            //form表单提交方式
            username =  request.getParameter("username");
            password =  request.getParameter("password");
            code =  request.getParameter("code");
            rKey =  request.getParameter("rkey");
        }
        //组装token
        //获取rkey中的值code值
        String codeValue = (String) redisTemplate.opsForValue().get("ck:"+rKey);
        if (!code.equals(codeValue)) {
            throw new RuntimeException("验证码错误");
        }
        //验证码通过，删除验证码
        redisTemplate.delete(codeValue);
        //生成token
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        //交给token管理器进行验证
        return this.getAuthenticationManager().authenticate(token);
    }

    /**
     * 认证失败处理方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        String returnData="";
        // 账号过期
        if (failed instanceof AccountExpiredException) {
            returnData="账号过期";
        }
        // 密码错误
        else if (failed instanceof BadCredentialsException) {
            returnData="密码错误";
        }
        // 密码过期
        else if (failed instanceof CredentialsExpiredException) {
            returnData="密码过期";
        }
        // 账号不可用
        else if (failed instanceof DisabledException) {
            returnData="账号不可用";
        }
        //账号锁定
        else if (failed instanceof LockedException) {
            returnData="账号锁定";
        }
        // 用户不存在
        else if (failed instanceof InternalAuthenticationServiceException) {
            returnData="用户不存在";
        }
        // 其他错误
        else{
            returnData="未知异常";
        }
        // 处理编码方式 防止中文乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        // 将反馈塞到HttpServletResponse中返回给前台
        R result = R.error(returnData);
        response.getWriter().write(new Gson().toJson(result));
    }
}
